/*
 * This code is a minimal hardware described in Chisel.
 * 
 * Blinking LED: the FPGA version of Hello World
 */

import chisel3._
import chisel3.util._


class WriterIO(size: Int) extends Bundle {
    val write = Input(Bool())
    val full = Output(Bool())
    val din = Input(UInt(size.W))
}

class ReaderIO(size: Int) extends Bundle {
    val read = Input(Bool())
    val empty = Output(Bool())
    val dout = Output(UInt(size.W))
}

class FIFORegister(size: Int) extends Module {
    val io = IO(new Bundle {
        val enq = new WriterIO(size)
        val deq = new ReaderIO(size)
    })
    
    val empty :: full :: Nil = Enum(2)
    val stateReg = RegInit(empty)
    val dataReg = RegInit(0.U(size.W))

    when(stateReg === empty) {
        when(io.enq.write) {
            stateReg := full
            dataReg := io.enq.din
        }
    } .elsewhen(stateReg === full) {
        when(io.deq.read) {
            stateReg := empty
            dataReg := 0.U      // 单纯方便在波形图中看是不是空了
        }
    } .otherwise {
        // 也应该没有“否则”的状态了
    }

    io.enq.full := stateReg === full
    io.deq.empty := stateReg === empty
    io.deq.dout := dataReg
}

class BubbleFifo(size: Int, depth: Int) extends Module {
    val io = IO(new Bundle {
        val enq = new WriterIO(size)
        val deq = new ReaderIO(size)
    })

    val buffers = Array.fill(depth) {
        Module(new FIFORegister(size))
    }

    for (i <- 0 until depth - 1) {
        buffers(i + 1).io.enq.din := buffers(i).io.deq.dout
        buffers(i + 1).io.enq.write := buffers(i).io.deq.read
        buffers(i).io.deq.read := ~buffers(i + 1).io.enq.full
    }

    io.enq <> buffers(0).io.enq
    io.deq <> buffers(depth - 1).io.deq
}

/**
 * An object extending App to generate the Verilog code.
 */
object BubbleFifo extends App {
  (new chisel3.stage.ChiselStage).emitVerilog(new FIFORegister(8))
  (new chisel3.stage.ChiselStage).emitVerilog(new BubbleFifo(8,8))
}
